<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use App\Models\Crontab;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use Cron\CronExpression;
use Illuminate\Support\Facades\DB;
use Symfony\Component\Process\Process;


class autoTask extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'autotask:run';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = '定时任务总调度';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
    	$crontab_list = $this->getTaskAll();
		$collection = collect($crontab_list);
        if(!$collection->isEmpty()){
            $multiplied = $collection->map(function ($item) {
                try {
	                // 分类执行任务
	                switch ($item->type) {
	                    case 'url':
	                        try {
	                            $client = new Client();
	                            $response = $client->request('GET', $value['contents']);
	                            $this->saveLog('url', $item->id, $item->title, 1, $item->contents . ' 请求成功，HTTP状态码: ' . $response->getStatusCode());
	                        } catch (RequestException $e) {
	                            $this->saveLog('url', $item->id, $item->title, 0, $item->contents . ' 请求成功失败: ' . $e->getMessage());
	                        }
	                        break;
	                    case 'sql':
	                        /* 注释中的方法可以一次执行所有SQL语句
	                        // 执行SQL
	                        $count  = DB::select($crontab['contents']);
	                        dump($count );*/
	
	                        // 解析成一条条的sql语句
	                        $sqls = str_replace("\r", "\n", $item->contents);
	                        $sqls = explode(";\n", $sqls);
	                        $remark = '';
	                        $status = 1;
	                        foreach ($sqls as $sql) {
	                            $sql = trim($sql);
	                            if (empty($sql)) continue;
	                            if (substr($sql, 0, 2) == '--') continue;   // SQL注释
	                            // 执行SQL并记录执行结果
	                            if (false !== DB::select($sql)) {
	                                $remark .= '执行成功: ' . $sql . "\r\n\r\n";
	                            } else {
	                                $remark .= '执行失败: ' . $sql . "\r\n\r\n";
	                                $status = 0;
	                            }
	                        }
	                        $this->saveLog('sql', $item->id, $item->title, $status, $remark);
	                        break;
	                    case 'shell':
	                        $status = 0;
	                        $request = 'fail';
	
	                        $process = new Process($item->contents);
	                        $process->run();
	                        if ($process->isSuccessful()) {
	                            $status = 1;
	                            $request = $process->getOutput();
	                        }
	                        $this->saveLog('shell', $item->id, $item->title, $status, $request);
	                        break;
	                }
	            }
	            catch (Exception $e)
	            {
	                $this->saveLog($item->type, $item->id, $item->title, 0, "执行的内容发生异常:\r\n" . $e->getMessage());
	            }
            });
            $this->info('执行任务-'.$multiplied->count().'-条！');
            return true;
        }else{
        	$this->error('没有查询到要执行的任务！');
            return false;
        }
    }
    
    private function getTaskAll()
    {
    	// 筛选未过期且未完成的任务
        $crontab_list = DB::Table('crontabs')->where(['status' => 'normal'])->orderBy('weigh','desc')->orderBy('id','desc')->get();
        if (!$crontab_list) {
        	$this->error('没有查询到要执行的任务！');
            return false;
        }
        $time = time();
        $collection = collect($crontab_list);
        $multiplied = $collection->map(function ($item) use ($time) {
        	$array		= [];
        	$execute = false;   // 是否执行
        	if (!$time < strtotime($item->begin_at)) {   //任务未开始
                if ($item->maximums && $item->executes > $item->maximums) {  //任务已超过最大执行次数
	                $array['status'] = 'completed';
	            } else if (strtotime($item->end_at) > 0 && $time > strtotime($item->end_at)) {     //任务已过期
	                $array['status'] = 'expired';
	            } else if ($item->executes > 0) {
	                $cron = CronExpression::factory($item->schedule);
	                /*
	                 * 根据当前时间判断是否该应该执行
	                 * 这个判断和秒数无关，其最小单位为分
	                 * 也就是说，如果处于该执行的这个分钟内如果多次调用都会判定为真
	                 * 所以我们在服务器上设置的定时任务最小单位应该是分
	                 */
	                if ($cron->isDue()) {
	                    // 允许执行
	                    $execute = true;
	                    // 允许执行的时候更新状态
	                    $array['execute_at'] = date('Y-m-d H:i:s');
	                    $array['executes'] = $item->executes + 1;
	                    $array['status'] = ($item->maximums > 0 && $item->executes >= $item->maximums) ? 'completed' : 'normal';
	                    DB::Table('crontabs')->where(['id' => $item->id])->update($array);
	                    return $item;
	                } 
	            } else {
            		// 允许执行
                    $execute = true;
                    // 允许执行的时候更新状态
                    $array['execute_at'] = date('Y-m-d H:i:s');
                    $array['executes'] = $item->executes + 1;
                    $array['status'] = ($item->maximums > 0 && $item->executes >= $item->maximums) ? 'completed' : 'normal';
                    DB::Table('crontabs')->where(['id' => $item->id])->update($array);
                    return $item;
	            }
            } 
        });
        return $multiplied->filter()->all();
    }

    // 保存运行日志
    private function saveLog($type, $cid, $title, $status, $remark = '')
    {
    	DB::table('crontab_logs')->insertGetId([
			'type'			=> $type,
			'cid'			=> $cid,
			'title'			=> $title,
			'status'		=> $status,
			'remark'		=> $remark,
			'created_at'    => date('Y-m-d H:i:s',time()),
            'updated_at'    => date('Y-m-d H:i:s',time()),
		]);
    }
}
